﻿using System;
using System.Drawing;

namespace dk.Helper.Base.Core
{
    public class WorldMapper : IWorldMapper
    {
        private readonly SolidBrush mapBrush = new SolidBrush(Color.FromArgb(50, 100, 30));
        private readonly IWorld world;
        private Rectangle clientRectangle;
        private Point offset;
        private int spaceBetweenVillages;

        /// <summary>
        /// Initializes a new instance of the <see cref="WorldMapper"/> class.
        /// </summary>
        /// <param name="world">The world.</param>
        public WorldMapper(IWorld world)
        {
            this.world = world;
        }

        private int WorldWidth
        {
            get { return clientRectangle.Width/(WorldMapSettings.Zoom + spaceBetweenVillages); }
        }

        private int WorldHeight
        {
            get { return clientRectangle.Height/(WorldMapSettings.Zoom + spaceBetweenVillages); }
        }

        #region IWorldMapper Members

        /// <summary>
        /// Gets or sets the world map settings.
        /// </summary>
        /// <value>
        /// The world map settings.
        /// </value>
        public IWorldMapSettings WorldMapSettings { get; set; }

        /// <summary>
        /// Creates the map.
        /// </summary>
        /// <param name="clientRectangle_in">The client rectangle.</param>
        /// <returns></returns>
        public Bitmap CreateMap(Rectangle clientRectangle_in)
        {
            clientRectangle = clientRectangle_in;
            spaceBetweenVillages = WorldMapSettings.Zoom > 2 ? 1 : 0;
            int worldWidth = clientRectangle.Width/(WorldMapSettings.Zoom + spaceBetweenVillages);
            int worldHeight = clientRectangle.Height/(WorldMapSettings.Zoom + spaceBetweenVillages);
            offset = new Point();
            offset.X = WorldMapSettings.MapCenterX - worldWidth/2;
            offset.Y = WorldMapSettings.MapCenterY - worldHeight/2;

            var bmp = new Bitmap(clientRectangle.Width, clientRectangle.Height);
            Graphics g = Graphics.FromImage(bmp);

            g.FillRectangle(mapBrush, 0, 0, bmp.Width, bmp.Height);

            if (world != null)
            {
                DrawContinentAndSectorLines(g);

                for (int x = offset.X; x < offset.X + worldWidth; x++)
                {
                    for (int y = offset.Y; y < offset.Y + worldHeight; y++)
                    {
                        IVillage village = world.Villages.VillagesByCoord[x, y];
                        if (village == null) continue;

                        if (WorldMapSettings.WorldMapFilter != null &&
                            !WorldMapSettings.WorldMapFilter.IsSatisfyingConditions(village)) continue;

                        int mapX = (village.Coord.X - offset.X)*(WorldMapSettings.Zoom + spaceBetweenVillages);
                        int mapY = (village.Coord.Y - offset.Y)*(WorldMapSettings.Zoom + spaceBetweenVillages);

                        g.FillRectangle(WorldMapSettings.GetVillageBrush(village), mapX, mapY, WorldMapSettings.Zoom,
                                        WorldMapSettings.Zoom);
                    }
                }
            }
            return bmp;
        }

        /// <summary>
        /// Gets the village at bitmap location
        /// </summary>
        /// <param name="point">The point on map (bitmap).</param>
        /// <returns></returns>
        public IVillage GetVillage(Point point)
        {
            if (WorldMapSettings.Zoom == 0) return null;
            int worldX = (point.X/(WorldMapSettings.Zoom + spaceBetweenVillages)) + offset.X;
            int worldY = (point.Y/(WorldMapSettings.Zoom + spaceBetweenVillages)) + offset.Y;

            var villageCoord = new Point(worldX, worldY);

            return world.Villages.VillagesByCoord[villageCoord.X, villageCoord.Y];
        }

        #endregion

        /// <summary>
        /// Draws the continent and sector lines.
        /// </summary>
        private void DrawContinentAndSectorLines(Graphics g)
        {
            //
            // vertical sector lines
            //
            int rem;
            int startX;
            Math.DivRem(offset.X, 5, out rem);
            if (rem == 0)
            {
                startX = offset.X;
            }
            else
            {
                startX = offset.X + 5 - rem;
            }

            int mapX;
            for (int i = startX; i < offset.X + WorldWidth; i += 5)
            {
                mapX = (i - offset.X)*(WorldMapSettings.Zoom + spaceBetweenVillages) - 1;

                g.DrawLine(new Pen(new SolidBrush(Color.FromArgb(33, 75, 34)), 1)
                           , new Point(mapX, 0)
                           , new Point(mapX, clientRectangle.Height));
            }

            //
            // horizontal sector lines
            //

            int startY;
            Math.DivRem(offset.Y, 5, out rem);
            if (rem == 0)
            {
                startY = offset.Y;
            }
            else
            {
                startY = offset.Y + 5 - rem;
            }

            int mapY;
            for (int i = startY; i < offset.Y + WorldHeight; i += 5)
            {
                mapY = (i - offset.Y)*(WorldMapSettings.Zoom + spaceBetweenVillages) - 1;

                g.DrawLine(new Pen(new SolidBrush(Color.FromArgb(33, 75, 34)), 1)
                           , new Point(0, mapY)
                           , new Point(clientRectangle.Width, mapY));
            }

            //
            // vertical continent lines
            //
            Math.DivRem(offset.X, 100, out rem);
            if (rem == 0)
            {
                startX = offset.X;
            }
            else
            {
                startX = offset.X + 100 - rem;
            }

            for (int i = startX; i < offset.X + WorldWidth; i += 100)
            {
                mapX = (i - offset.X)*(WorldMapSettings.Zoom + spaceBetweenVillages) - 1;

                g.DrawLine(new Pen(new SolidBrush(Color.Black), 1)
                           , new Point(mapX, 0)
                           , new Point(mapX, clientRectangle.Height));
            }


            //
            // horizontal continent lines
            //
            Math.DivRem(offset.Y, 100, out rem);
            if (rem == 0)
            {
                startY = offset.Y;
            }
            else
            {
                startY = offset.Y + 100 - rem;
            }

            for (int i = startY; i < offset.Y + WorldHeight; i += 100)
            {
                mapY = (i - offset.Y)*(WorldMapSettings.Zoom + spaceBetweenVillages) - 1;

                g.DrawLine(new Pen(new SolidBrush(Color.Black), 1)
                           , new Point(0, mapY)
                           , new Point(clientRectangle.Width, mapY));
            }
        }
    }
}